﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace KnBinarySerializer.Field
{
    internal class ClassSerialContext
    {
        /// <summary>
        /// 一般为读取时使用
        /// </summary>
        Dictionary<short, ClassSerial> readTypes = new Dictionary<short, ClassSerial>();
        /// <summary>
        /// 一般为写入时使用
        /// </summary>
        Dictionary<Type, ClassSerial> types = new Dictionary<Type, ClassSerial>();
        /// <summary>
        /// 类的字段
        /// </summary>
        static Dictionary<Type, Dictionary<string, IClassField>> dictField = new Dictionary<Type, Dictionary<string, IClassField>>();
        /// <summary>
        /// 类型的构造函数
        /// </summary>
        static Dictionary<Type, Func<object>> dictCreators = new Dictionary<Type, Func<object>>();        
        static object _lockObj = new object();

        public ClassSerial ReadClassSerial(BinarySerializeManager content)
        {
            //读取信息
            var serialNo = content.ReadShort();
            if (!readTypes.ContainsKey(serialNo))
            {                
                //读取字段总数
                int cnt = (int)content.ReadShort();
                ClassSerial classSerial = new ClassSerial();
                classSerial.SerialNo = serialNo;                
                for (int i = 0; i < cnt; i++)
                {
                    //classSerial.Fields[i] = content.DeSerializeObject<string>();
                    classSerial.SetField(i, content.DeSerializeObject<string>());
                }
                readTypes.Add(serialNo, classSerial);
            }
            return readTypes[serialNo];
        }
        public ClassSerial WriteClassSerial(BinarySerializeManager stream, Type type, Dictionary<string, IClassField> fields)
        {
            if (types.ContainsKey(type))
            {
                stream.Write(types[type].SerialNo);//写入序号
                return types[type];
            }
            var clsSerial = new ClassSerial();
            clsSerial.SerialNo = (short)types.Count;
            //写入            
            stream.Write(clsSerial.SerialNo);//写入序号
            stream.Write((short)fields.Count);//写入字段总数
            int index = -1;
            foreach (var field in fields)
            {
                index++;
                clsSerial.Fields.Add(index, field.Key);
                stream.SerializeObject(field.Key);//将字段名顺序写入
            }
            types[type] = clsSerial;
            dictField[type] = fields;           
            return clsSerial;
        }
        /// <summary>
        /// 写入时一般使用本方法。获取次序
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public (ClassSerial, Dictionary<string, IClassField>) GetClassSerial(Type type)
        {
            if (types.ContainsKey(type)) return (types[type], dictField[type]);

            var serial = new ClassSerial();
            serial.SerialNo = (short)types.Count;

            Dictionary<string, IClassField> fields = GetFields(type);
            int index = -1;
            foreach (var field in fields)
            {
                index++;
                serial.Fields.Add(index, field.Key);
            }
            types[type] = serial;
            dictField[type] = fields;            
            return (serial, fields);
        }
        public static Dictionary<string, IClassField> GetFields(Type type)
        {
            lock (_lockObj)
            {
                if (!dictField.ContainsKey(type))
                    dictField[type] = getTypeFields(type);                
                return dictField[type];
            }
        }
        public static object CreateObject(Type type)
        {
            Func<object> func = null;
            lock (_lockObj)
            {
                if (!dictCreators.ContainsKey(type))
                    dictCreators[type] = MethodHelper.CreateDefaultConstructor<object>(type);
                func = dictCreators[type];
            }
            return func();
        }
        private static Dictionary<string, IClassField> getTypeFields(Type type)
        {
            Dictionary<string, IClassField> result = new Dictionary<string, IClassField>();
            PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo property in propertyInfos)
            {
                if (property.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length > 0)
                    continue;
                MethodInfo method = property.GetGetMethod();
                if (method != null && method.Name == "get_Item")
                    continue;
                result.Add(property.Name, new ClassProperty(property));
            }

            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
            foreach (FieldInfo field in fields)
            {
                if (field.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length > 0)
                    continue;
                result.Add(field.Name, new ClassField(field));
            }
            return result;
        }
        /// <summary>
        /// 读取时，从序号中获取字段顺序
        /// </summary>
        /// <param name="serialNo"></param>
        /// <returns></returns>
        public ClassSerial GetClassSerial(short serialNo)
        {
            if (!readTypes.ContainsKey(serialNo))
                throw new KnBinaryException(0, $"未找到类型[{serialNo}]");
            return readTypes[serialNo];
        }

        long flotPosition;
        /// <summary>
        /// 在当前位置预写入记录的位置。一般都会有前部
        /// </summary>
        /// <param name="stream"></param>
        public void WriteFlot(BinarySerializeManager stream)
        {
            flotPosition = stream.GetPosition();
            stream.Write((int)0);//预占位置
        }
        public void WriteSerial(BinarySerializeManager stream)
        {
            int serialStart = (int)stream.GetPosition();//减少一点占用。使用32位长度
            stream.SetPosition(flotPosition);
            stream.Write(serialStart);//将serialStart写入预占的位置。以备反序化时，读取类的序列化信息。
            stream.SetPosition(serialStart);            
            int index = -1;
            foreach (var tp in types)
            {
                index++;
                if (tp.Value.SerialNo != index)
                    throw new KnBinaryException(0, "写入次序错误");
                var fields = tp.Value.Fields;
                stream.Write((short)fields.Count);//写入字段总数
                for (int i = 0; i < fields.Count; i++)
                    stream.SerializeObject(fields[i]);//将字段名顺序写入
            }
        }

        public void ReadSerial(BinarySerializeManager content)
        {
            long serialStart = content.ReadInt();
            long lastPosition = content.GetPosition();//保存最后位置
            content.SetPosition(serialStart);//转到类型记录的位置

            short index = -1;
            while (!content.IsEnd())
            {
                index++;
                ClassSerial classSerial = new ClassSerial();
                classSerial.SerialNo = index;
                classSerial.Fields = new Dictionary<int, string>();
                //读取总数
                int cnt =(int)content.ReadShort();
                for(int i = 0; i < cnt; i++)
                {
                    classSerial.Fields[i] = content.DeSerializeObject<string>();
                }
                readTypes.Add(index, classSerial);
            }
            content.SetPosition(lastPosition);//恢复原位置
        }

    }
}
